﻿using System;
using System.Buffers;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO.Pipes;
using System.Linq;
using System.Threading;
using UMC.Data;
using UMC.Net;
using System.Net;
using System.Threading.Tasks;
using System.Threading.Channels;
using System.Net.Sockets;
using Microsoft.VisualBasic;
using UMC.ITME;

namespace UMC.Bridge
{
    public interface INetBridgePrepare
    {
        void Prepare(byte[] bytes, int offset, int size);
    }
    public class HttpBridgeServier : NetBridge
    {
        public static NetHttpRequest Sign(NetHttpRequest http)
        {

            return Sign(http, Web.WebResource.Instance().Provider["appSecret"]);
        }
        public static NetHttpRequest Sign(NetHttpRequest http, String secret)
        {
            http.Headers.Add(new HeadValue("umc-client-pfm", "sync"));
            http.Headers.Add(new HeadValue("umc-request-time", UMC.Data.Utility.TimeSpan().ToString()));
            if (String.IsNullOrEmpty(secret) == false)
            {
                http.Headers.Add(new HeadValue("umc-request-sign", UMC.Data.Utility.Sign(http.Headers, "umc-"u8, secret)));
            }

            return http;
        }

        public static async Task<String> CName(StringValue hostName)
        {
            var cName = hostName.UTF8();
            try
            {
                var enity = await Dns.GetHostEntryAsync(cName);
                cName = enity.HostName;
            }
            catch { }
            return cName;
        }
        public int Key
        {
            get;
            internal set;
        }
        public HttpBridgeServier(int bridgeIndex)
        {
            this.BridgeIndex = bridgeIndex;
        }
        public override void Close()
        {
            RemoveBridgeServer(this);

            var cs = this._Clients.Values.ToArray();
            foreach (var c in cs)
            {
                (c as IDisposable)?.Dispose();
            }
            this._Clients.Clear();
            base.Close();
        }
        class NetBridgePool
        {

            public int Count
            {
                get
                {
                    return this.Bridges.Values.Sum(r => r._Clients.Count);
                }
            }
            public int Key
            {
                get; set;
            }

            internal ConcurrentDictionary<int, HttpBridgeServier> Bridges = new ConcurrentDictionary<int, HttpBridgeServier>();
        }
        static ConcurrentDictionary<int, NetBridgePool> _POOL = new ConcurrentDictionary<int, NetBridgePool>();
        public static void RemoveBridgeServer(int key, int bridgeIndex)
        {
            NetBridgePool pool;
            if (_POOL.TryGetValue(key, out pool))
            {
                if (pool.Bridges.TryGetValue(bridgeIndex, out var bs))
                {
                    RemoveBridgeServer(bs);
                }
            }
        }
        public static void RemoveBridgeServer(HttpBridgeServier bridgeServer)
        {
            try
            {
                foreach (var m in bridgeServer._Clients.Values)
                {
                    (m as IDisposable)?.Dispose();
                }
                bridgeServer._Clients.Clear();

                if (_POOL.TryGetValue(bridgeServer.Key, out var pool))
                {
                    pool.Bridges.TryRemove(bridgeServer.BridgeIndex, out var _);

                    if (pool.Bridges.Count == 0)
                    {
                        _POOL.TryRemove(bridgeServer.Key, out pool);
                    }

                }
            }
            catch
            {

            }
        }
        public static void Check()
        {
            var vs = _POOL.Values.ToArray();
            foreach (var b in vs)
            {
                var bs = b.Bridges.Values.ToArray();

                var now = UMC.Data.Utility.TimeSpan();
                foreach (var nb in bs)
                {
                    if (now > nb.ActiveTime + 10)
                        nb.Write(0, Array.Empty<byte>(), 0, 0);
                }
            }
        }
        public int BridgeIndex
        {
            get;
            private set;
        }
        public static void AppendServer(int key, HttpBridgeServier bridgeServer)
        {
            bridgeServer.Key = key;
            NetBridgePool pool = _POOL.GetOrAdd(key, r => new NetBridgePool() { Key = key });
            RemoveBridgeServer(key, bridgeServer.BridgeIndex);
            pool.Bridges.TryAdd(bridgeServer.BridgeIndex, bridgeServer);

        }
        public static bool CheckBridgeServer(int key)
        {
            // int k;
            return CheckBridgeServer(key, out var k);
        }
        public static bool CloseBridgeServer(int key)
        {
            NetBridgePool pool;
            if (_POOL.TryGetValue(key, out pool))
            {
                var bs = pool.Bridges.Values.ToArray();
                foreach (var b in bs)
                {
                    try
                    {

                        b.Close();
                    }
                    catch
                    {

                    }
                }
                pool.Bridges.Clear();
                return true;
            }
            return false;
        }


        // async void Receive(ChannelReader<Tuple<int, long, int>> reader)
        // {
        //     var buffer = ArrayPool<byte>.Shared.Rent(400);
        //     try
        //     {
        //         while (await reader.WaitToReadAsync())
        //         {
        //             if (reader.TryRead(out var result))
        //             {
        //                 if (result.Item3 > 0)
        //                 {
        //                     if (Clients.TryGetValue(result.Item1, out var proxy))
        //                     {

        //                         using (var view = NetCache.Share.CreateAccessor(result.Item2, result.Item3))
        //                         {
        //                             int size = 0;
        //                             while ((size = view.Read(buffer, 0, buffer.Length)) > 0)
        //                             {
        //                                 proxy.Receive(buffer, 0, size);
        //                             }
        //                         }

        //                         if (proxy.IsOver)
        //                         {
        //                             Clients.TryRemove(result.Item1, out proxy);
        //                         }
        //                     }
        //                 }
        //                 else if (Clients.TryRemove(result.Item1, out var mimeBody))
        //                 {
        //                     if (mimeBody is Mime)
        //                         ((Mime)mimeBody).ReceiveException(new Exception("穿透传输中止"));

        //                 }


        //             }
        //         }
        //     }
        //     catch
        //     {

        //     }
        //     finally
        //     {

        //         ArrayPool<byte>.Shared.Return(buffer);
        //     }
        //     this.Close();

        // }


        //
        //
        public static bool CheckBridgeServer(int key, out HttpBridgeServier bridgeServier)
        {
            bridgeServier = null;
            NetBridgePool pool;
            if (_POOL.TryGetValue(key, out pool))
            {
                bridgeServier = pool.Bridges.Values.MinBy(p => p._Clients.Count);
                return true;
            }
            return false;
        }
        public static int GetBridgeServers(int key)
        {
            if (_POOL.TryGetValue(key, out var pool))
            {
                return pool.Bridges.Count;//.Values.MinBy(p => p.Clients.Count);
            }
            return 0;

        }
        static int _pid = 0;
        // var postion =//, cout) - cout;
        public static int IncrementId => Interlocked.Increment(ref _pid);
        public static async Task<DataWriter> BridgeStart(IPAddress ipAddresses, int port, Memory<byte> header, INetBridgeReader httpMime)
        {
            var ls = System.Buffers.ArrayPool<byte>.Shared.Rent(410);
            HashCode hashCode = new HashCode();
            // IPAddress.t
            ipAddresses.TryWriteBytes(ls, out var bytesWritten);
            hashCode.AddBytes(ls.AsSpan(0, bytesWritten));//domain.WriteBytes(ls, 0)));

            hashCode.AddBytes(port.StringValue().Span);
            var key = hashCode.ToHashCode();
            HttpBridgeServier bridgeServier;
            try
            {
                if (_POOL.TryGetValue(key, out var bridgePool) == false || bridgePool.Bridges.Count <5)
                {
                    var timeOut = new CancellationTokenSource(5000);
                    Socket proxySocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    await proxySocket.ConnectAsync(ipAddresses, port, timeOut.Token);
                    ls[0] = 0x02;
                    ls[1] = 0x01;
                    ls[2] = 0x00;
                    ls[3] = (byte)WebServlet.AppIdKey.Length;
                    int size = 4;
                    WebServlet.AppIdKey.Span.CopyTo(ls.AsSpan(size));
                    size += WebServlet.AppIdKey.Length;
                    ls[size] = 0x00;
                    size++;
                    await proxySocket.SendAsync(new ArraySegment<byte>(ls, 0, size), timeOut.Token);

                    size = await proxySocket.ReceiveAsync(new ArraySegment<byte>(ls, 0, ls.Length), timeOut.Token);
                    if (ls[0] != 0x00)
                    {
                        return null;
                    }

                    bridgeServier = new HttpBridgeServier(0);
                    bridgeServier.Bridge(proxySocket);
                    AppendServer(key, bridgeServier);
                    // return b;
                }
                else
                {
                    bridgeServier = bridgePool.Bridges.Values.MinBy(p => p._Clients.Count);
                }

                var pid = Interlocked.Increment(ref _pid);
                ls[0] = 0x05;
                BitConverter.TryWriteBytes(ls.AsSpan(1), pid);
                BitConverter.TryWriteBytes(ls.AsSpan(5), header.Length);
                header.Span.CopyTo(ls.AsSpan(9));
                bridgeServier._Clients.TryAdd(pid, httpMime);
                bridgeServier.Write(ls, 0, header.Length + 9);
                return bridgeServier.Write(pid);
            }
            finally
            {

                System.Buffers.ArrayPool<byte>.Shared.Return(ls);
            }
        }
        public static DataWriter BridgeStart(int key, Span<byte> header, INetBridgeReader httpMime)
        {
            NetBridgePool bridgePool;
            if (_POOL.TryGetValue(key, out bridgePool) && bridgePool.Bridges.Count > 0)
            {
                var pid = Interlocked.Increment(ref _pid);
                var ls = System.Buffers.ArrayPool<byte>.Shared.Rent(410);
                try
                {
                    ls[0] = 0x05;
                    BitConverter.TryWriteBytes(ls.AsSpan(1), pid);
                    BitConverter.TryWriteBytes(ls.AsSpan(5), header.Length);
                    header.CopyTo(ls.AsSpan(9));
                    var server = bridgePool.Bridges.Values.MinBy(p => p._Clients.Count);
                    server._Clients.TryAdd(pid, httpMime);
                    server.Write(ls, 0, header.Length + 9);
                    return server.Write(pid);
                }
                catch
                {
                    CloseBridgeServer(key);
                }
                finally
                {

                    System.Buffers.ArrayPool<byte>.Shared.Return(ls);
                }
            }
            return null;
        }
        public static DataWriter BridgeServer(int key, int pid, INetBridgeReader httpMime)
        {
            NetBridgePool bridgePool;
            if (_POOL.TryGetValue(key, out bridgePool) && bridgePool.Bridges.Count > 0)
            {
                try
                {
                    var server = bridgePool.Bridges.Values.MinBy(p => p._Clients.Count);
                    server._Clients.TryAdd(pid, httpMime);
                    return server.Write(pid);
                }
                catch
                {
                    CloseBridgeServer(key);
                }
            }
            return null;
        }

        public void Bridge(byte[] data, int offset, int count)
        {
            this.Write(data, offset, count);
        }
        public ConcurrentDictionary<int, INetBridgeReader> Clients => _Clients;//.Count;

        ConcurrentDictionary<int, INetBridgeReader> _Clients = new ConcurrentDictionary<int, INetBridgeReader>();

       
        protected override void Prepare(int pid, byte[] buffer, int index, int length)
        {
            if (Clients.TryGetValue(pid, out var proxy))
            {
                try
                {
                    if (proxy is INetBridgePrepare)
                    {
                        ((INetBridgePrepare)proxy).Prepare(buffer, index, length);
                    }
                    else
                    {
                        proxy.Receive(buffer, index, length);
                        if (proxy.IsOver)
                        {

                            Clients.TryRemove(pid, out var _);
                        }

                    }
                }
                catch //(Exception ex)
                {
                    Clients.TryRemove(pid, out var _);
                    this.Write(pid, Array.Empty<byte>(), 0, 0);
                }
            }
        }
        protected override void Read(int pid, byte[] buffer, int index, int length)
        {
            if (length == 0)
            {
                if (Clients.TryRemove(pid, out var mimeBody))
                {
                    mimeBody.Receive(buffer, index, length);
                }
            }
            else if (Clients.TryGetValue(pid, out var proxy))
            {
                try
                {
                    proxy.Receive(buffer, index, length);
                    if (proxy.IsOver)
                    {
                        Clients.TryRemove(pid, out proxy);
                    }
                }
                catch //(Exception ex)
                {
                    if (proxy is IDisposable)
                    {
                        ((IDisposable)proxy).Dispose();
                    }
                    Clients.TryRemove(pid, out var _);
                    this.Write(pid, Array.Empty<byte>(), 0, 0);
                }
            }
        }
    }


}
